#include "dht11.h"

/*!
 *  @brief  Instantiates a new DHT class
 *  @param  pin
 *          pin number that sensor is connected
 */
DHT11::DHT11(uint8_t pin) 
{
  _pin = pin;
  pinMode(_pin, INPUT_PULLUP);

}

/*!
 *  @brief  Reset the specific buffer
 *  @param  buf
 *          buffer that you want to reset
 *  @param  size
 *          how many bytes you want to reset
 */
void DHT11::resetBuffer(uint8_t *buf, uint8_t size)
{
  for (int i=0; i<size; i++) {
    buf[i] = 0;
  }
}

/*!
 *  @brief  Read value from sensor and update data array.
 *  @param  usec
 *          Optionally pass pull-up time (in microseconds) before DHT reading
 *          starts. Default is 55 (see function declaration in dht.h).
 *  @return If read data successfully return DHTLIB_OK,
 *          if the data are not complete return DHTLIB_ERROR_CHECKSUM,
 *          if read timeout return DHTLIB_ERROR_TIMEOUT.
 */
uint8_t DHT11::read(uint8_t usec)
{
  uint8_t bit = 7, index = 0;
  uint32_t t1 = 0, t2 = 0, delta = 0;

  // Reset data buffer
  resetBuffer(data, sizeof(data)/sizeof(data[0]));

  // MCU request sampling
  pinMode(_pin, OUTPUT);
  digitalWrite(_pin, LOW);
  delay(BEGIN_TIME);

  digitalWrite(_pin, HIGH);
  delayMicroseconds(GO_TIME);
  pinMode(_pin, INPUT);

  // Waiting for DHT11 sensor reply
  // ACKNOWLEDGE or TIMEOUT
  unsigned int loopCnt = 10000;
  while(digitalRead(_pin) == LOW)
    if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

  loopCnt = 10000;
  while(digitalRead(_pin) == HIGH)
    if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

  // READ OUTPUT - 40 BITS => 5 BYTES or TIMEOUT
  for (int i=0; i<40; i++)
  {
    loopCnt = 10000;
    while(digitalRead(_pin) == LOW)
      if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

    t1 = micros();

    loopCnt = 10000;
    while(digitalRead(_pin) == HIGH)
      if (loopCnt-- == 0) return DHTLIB_ERROR_TIMEOUT;

    t2 = micros();
    if(t2 < t1) delta = UINT32_MAX - t1 + t2;
    else delta = t2 - t1;

    if(delta > THINK_TIME) {
      data[index] |= (1 << bit);
    }

    // Next byte or not
    if (bit == 0) {
      bit = 7;
      index++;
    }
    else bit--;
  }

  if (data[4] != checksum()) {
    return DHTLIB_ERROR_CHECKSUM;
  }

  return DHTLIB_OK;
}

/*!
 *  @brief  Calculate checksum
 *  @return The last 8 bit of checksum value
 */
uint8_t DHT11::checksum()
{
  uint16_t sum = 0;

  for(int i=0; i<sizeof(data)/sizeof(data[0]); i++) {
    sum += data[i];
  }
  return (uint8_t)sum;
}

/*!
 *  @brief  Read Humidity
 *  @return float value - humidity in percent
 */
float DHT11::getHumidity()
{
  float f = data[0] + data[1] * 0.1;
  return f;
}

/*!
 *  @brief  Get temperature
 *  @param  isFahrenheit
 *          If or not scale in Fahrenheit (default in Celcius)
 *  @return Temperature value in selected scale
 */
float DHT11::getTemperature(bool isFahrenheit)
{
  float f = data[2];
    if (data[3] & 0x80) {
      f = -1 - f;
    }
    f += (data[3] & 0x0f) * 0.1;

    if (isFahrenheit) {
      f = convertCtoF(f);
    }
    return f;
}

/*!
 *  @brief  Converts Celcius to Fahrenheit
 *  @param  c
 *      value in Celcius
 *  @return float value in Fahrenheit
 */
float DHT11::convertCtoF(float c) 
{ 
  return c * 1.8 + 32; 
}

/*!
 *  @brief  Converts Fahrenheit to Celcius
 *  @param  f
 *      value in Fahrenheit
 *  @return float value in Celcius
 */
float DHT11::convertFtoC(float f) 
{ 
  return (f - 32) * 0.55555; 
}

/*!
 *  @brief  Converts Celcius to Kelvin
 *  @param  c
 *      value in Celcius
 *  @return float value in Kelvin
 */
float DHT11::convertCtoK(float c)
{
        return c + 273.15;
}

/*!
 *  @brief  Calculate the dew point
 *  @param  celsius
 *          temperature value in Celcius
 *  @param  humidity
 *          humidity value in percent
 */
double DHT11::dewPoint(double celsius, double humidity)
{
        double A0= 373.15/(273.15 + celsius);
        double SUM = -7.90298 * (A0-1);
        SUM += 5.02808 * log10(A0);
        SUM += -1.3816e-7 * (pow(10, (11.344*(1-1/A0)))-1);
        SUM += 8.1328e-3 * (pow(10,(-3.49149*(A0-1)))-1);
        SUM += log10(1013.246);
        double VP = pow(10, SUM-3) * humidity;
        double T = log(VP/0.61078);   // temp var
        return (241.88 * T) / (17.558-T);
}

/*!
 *  @brief  Calculate the dew point fast
 *  @param  celsius
 *          temperature value in Celcius
 *  @param  humidity
 *          humidity value in percent
 */
double DHT11::dewPointFast(double celsius, double humidity)
{
        double a = 17.271;
        double b = 237.7;
        double temp = (a * celsius) / (b + celsius) + log(humidity/100);
        double Td = (b * temp) / (a - temp);
        return Td;
}

/*!
 *  @brief  Compute Heat Index
 *          Using both Rothfusz and Steadman's equations
 *          (http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml)
 *  @param  temperature
 *          temperature in selected scale
 *  @param  percentHumidity
 *          humidity in percent
 *  @param  isFahrenheit
 *          true if fahrenheit, false if celcius
 *  @return float heat index
 */
float DHT11::computeHeatIndex(float temperature, float percentHumidity,
                              bool isFahrenheit) {
  float hi;

  if (!isFahrenheit)
    temperature = convertCtoF(temperature);

  hi = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) +
              (percentHumidity * 0.094));

  if (hi > 79) {
    hi = -42.379 + 2.04901523 * temperature + 10.14333127 * percentHumidity +
         -0.22475541 * temperature * percentHumidity +
         -0.00683783 * pow(temperature, 2) +
         -0.05481717 * pow(percentHumidity, 2) +
         0.00122874 * pow(temperature, 2) * percentHumidity +
         0.00085282 * temperature * pow(percentHumidity, 2) +
         -0.00000199 * pow(temperature, 2) * pow(percentHumidity, 2);

    if ((percentHumidity < 13) && (temperature >= 80.0) &&
        (temperature <= 112.0))
      hi -= ((13.0 - percentHumidity) * 0.25) *
            sqrt((17.0 - abs(temperature - 95.0)) * 0.05882);

    else if ((percentHumidity > 85.0) && (temperature >= 80.0) &&
             (temperature <= 87.0))
      hi += ((percentHumidity - 85.0) * 0.1) * ((87.0 - temperature) * 0.2);
  }

  return isFahrenheit ? hi : convertFtoC(hi);
}
